package com.spa.infrastructure.repository.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.google.common.collect.Lists;
import com.spa.infrastructure.enums.dict.base.WhetherTypeEnum;
import com.spa.infrastructure.enums.dict.order.OrderOperStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderStatusEnum;
import com.spa.infrastructure.repository.condition.OrderInfoCondition;
import com.spa.infrastructure.repository.mapper.OrderInfoMapper;
import com.spa.infrastructure.repository.po.OrderInfoPO;
import com.spa.infrastructure.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class OrderInfoRepositoryService extends MPJBaseServiceImpl<OrderInfoMapper, OrderInfoPO> {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    public OrderInfoPO getOrderInfoOne(OrderInfoCondition condition) {
        return getOne(wrapper(condition));
    }

    public int insertOrder(OrderInfoPO infoPO) {
        return orderInfoMapper.insert(infoPO);
    }

    public boolean orderExists(OrderInfoCondition condition) {
        LambdaQueryWrapper<OrderInfoPO> query = wrapper(condition);
        return orderInfoMapper.exists(query);
    }

    public List<OrderInfoPO> list(OrderInfoCondition condition) {
        LambdaQueryWrapper<OrderInfoPO> wrapper = wrapper(condition);
        return list(wrapper);
    }

    public Page<OrderInfoPO> page(OrderInfoCondition condition) {
        return page(Page.of(condition.getCurrent(), condition.getSize()), wrapper(condition));
    }

    public OrderInfoPO getOrderInfoById(Long id) {
        return getById(id);
    }

    public Long countByCondition(OrderInfoCondition condition) {
        return count(wrapper(condition));
    }

    /**
     * 通过指定条件统计订单数，按照用户去重
     * @param condition
     * @return
     */
    public long countByConditionDistinctUser(OrderInfoCondition condition) {
        QueryWrapper<OrderInfoPO> wrapper = new QueryWrapper<>();
        wrapper.select("distinct user_id");
        wrapper.eq(ObjectUtil.isNotNull(condition.getStatus()), "status", condition.getStatus());
        return count(wrapper);
    }

    /**
     * 按门店分组查询
     * @param condition
     * @return
     */
    public List<OrderInfoPO> listGroupByShop(OrderInfoCondition condition) {
        QueryWrapper<OrderInfoPO> wrapper = new QueryWrapper<>();
        wrapper.select("*, COUNT(*) AS order_count, SUM(order_price) AS total_amount");
        wrapper.eq(ObjectUtil.isNotNull(condition.getStatus()), "status", condition.getStatus());
        wrapper.ge("create_time", condition.getBeginDate());
        wrapper.le("create_time", condition.getEndDate());
        wrapper.groupBy("shop_id");
        wrapper.orderByDesc("order_count");
        return list(wrapper);
    }

    /**
     * 按项目分组查询
     * @param condition
     * @return
     */
    public List<OrderInfoPO> listGroupByService(OrderInfoCondition condition) {
        QueryWrapper<OrderInfoPO> wrapper = new QueryWrapper<>();
        wrapper.select("*, COUNT(*) AS order_count, SUM(order_price) AS total_amount");
        wrapper.eq(ObjectUtil.isNotNull(condition.getStatus()), "status", condition.getStatus());
        wrapper.ge("create_time", condition.getBeginDate());
        wrapper.le("create_time", condition.getEndDate());
        wrapper.groupBy("service_id");
        wrapper.orderByDesc("order_count");
        return list(wrapper);
    }

    /**
     * 按用户分组查询
     * @param condition
     * @return
     */
    public List<OrderInfoPO> listGroupByUser(OrderInfoCondition condition) {
        QueryWrapper<OrderInfoPO> wrapper = new QueryWrapper<>();
        wrapper.select("*, COUNT(*) AS order_count, SUM(order_price) AS total_amount");
        wrapper.eq(ObjectUtil.isNotNull(condition.getStatus()), "status", condition.getStatus());
        wrapper.ge("create_time", condition.getBeginDate());
        wrapper.le("create_time", condition.getEndDate());
        wrapper.groupBy("user_id");
        wrapper.orderByDesc("order_count");
        return list(wrapper);
    }

    /**
     * 按技师分组查询
     * @param condition
     * @return
     */
    public List<OrderInfoPO> listGroupByMassagist(OrderInfoCondition condition) {
        QueryWrapper<OrderInfoPO> wrapper = new QueryWrapper<>();
        wrapper.select("*, COUNT(*) AS order_count, SUM(order_price) AS total_amount");
        wrapper.eq(ObjectUtil.isNotNull(condition.getStatus()), "status", condition.getStatus());
        wrapper.ge("create_time", condition.getBeginDate());
        wrapper.le("create_time", condition.getEndDate());
        wrapper.groupBy("massagist_id");
        wrapper.orderByDesc("order_count");
        return list(wrapper);
    }

    public boolean updateByCondition(OrderInfoPO po) {
        return update(po, updateWrapper(po));
    }

    public List<Map<String, Object>> countByMassagistIds(List<Long> ids) {
        return baseMapper.selectMaps(countWrapper(ids));
    }

    private QueryWrapper<OrderInfoPO> countWrapper(List<Long> ids) {
        QueryWrapper<OrderInfoPO> countWrapper = new QueryWrapper<>();
        countWrapper.groupBy("massagist_id");
        countWrapper.select("massagist_id","count(id) count");
        countWrapper.in(CollectionUtil.isNotEmpty(ids), "massagist_id", ids);
        return countWrapper;
    }

    private LambdaQueryWrapper<OrderInfoPO> wrapper(OrderInfoCondition condition) {
        LambdaQueryWrapper<OrderInfoPO> wrapper = Wrappers.lambdaQuery();
        wrapper.in(CollectionUtil.isNotEmpty(condition.getMassgistIds()), OrderInfoPO::getMassagistId, condition.getMassgistIds());
        wrapper.in(CollectionUtil.isNotEmpty(condition.getStatusList()), OrderInfoPO::getStatus, condition.getStatusList());
        wrapper.in(CollectionUtil.isNotEmpty(condition.getOrderOperStatusList()), OrderInfoPO::getOrderOperStatus, condition.getOrderOperStatusList());
        wrapper.in(CollectionUtil.isNotEmpty(condition.getOrderSubStatusList()), OrderInfoPO::getSubStatus, condition.getOrderSubStatusList());
        wrapper.in(CollectionUtil.isNotEmpty(condition.getUserIds()), OrderInfoPO::getUserId, condition.getUserIds());
        wrapper.in(CollectionUtil.isNotEmpty(condition.getShopIds()), OrderInfoPO::getShopId, condition.getShopIds());
        wrapper.eq(ObjectUtil.isNotNull(condition.getStatus()), OrderInfoPO::getStatus, condition.getStatus());
        wrapper.eq(ObjectUtil.isNotNull(condition.getOrderId()), OrderInfoPO::getId, condition.getOrderId());
        wrapper.eq(ObjectUtil.isNotNull(condition.getMassagistId()), OrderInfoPO::getMassagistId, condition.getMassagistId());
        wrapper.eq(ObjectUtil.isNotNull(condition.getIsTransfer()), OrderInfoPO::getIsTransfer, condition.getIsTransfer());
        wrapper.ge(StringUtils.isNotEmpty(condition.getBeginDate()), OrderInfoPO::getCreateTime, condition.getBeginDate());
        wrapper.le(StringUtils.isNotEmpty(condition.getEndDate()), OrderInfoPO::getCreateTime, condition.getEndDate());
        wrapper.ge(StringUtils.isNotEmpty(condition.getEndTimeFrom()), OrderInfoPO::getEndTime, condition.getEndTimeFrom());
        wrapper.le(StringUtils.isNotEmpty(condition.getEndTimeTo()), OrderInfoPO::getEndTime, condition.getEndTimeTo());
        wrapper.eq(ObjectUtil.isNotNull(condition.getServiceId()), OrderInfoPO::getServiceId, condition.getServiceId());
        wrapper.eq(ObjectUtil.isNotNull(condition.getShopId()), OrderInfoPO::getShopId, condition.getShopId());
        wrapper.ge(StringUtils.isNotEmpty(condition.getBeginAppointTime()), OrderInfoPO::getAppointTime, condition.getBeginAppointTime());
        wrapper.le(StringUtils.isNotEmpty(condition.getEndAppointTime()), OrderInfoPO::getAppointTime, condition.getEndAppointTime());
        wrapper.ne(ObjectUtil.isNotNull(condition.getNeStatus()), OrderInfoPO::getStatus, condition.getNeStatus());
        wrapper.in(ObjectUtil.isNotNull(condition.getEffectiveFlag()), OrderInfoPO::getStatus, Lists.newArrayList(OrderStatusEnum.$20.getCode(), OrderStatusEnum.$30.getCode(), OrderStatusEnum.$40.getCode()));
        wrapper.eq(ObjectUtil.isNotEmpty(condition.getUserId()), OrderInfoPO::getUserId, condition.getUserId());
        wrapper.eq(ObjectUtil.isNotEmpty(condition.getOrderNo()), OrderInfoPO::getOrderCode, condition.getOrderNo());
        wrapper.eq(OrderInfoPO::getIsDel, ObjectUtil.isNotEmpty(condition.getIsDel())?condition.getIsDel(): WhetherTypeEnum.$0.getCode());
        wrapper.notIn(CollUtil.isNotEmpty(condition.getNeStatusList()),OrderInfoPO::getStatus,condition.getNeStatusList());
        wrapper.le(ObjectUtil.isNotNull(condition.getExpiryTime()), OrderInfoPO::getExpiryTime, condition.getExpiryTime());
        wrapper.le(ObjectUtil.isNotNull(condition.getPayTime()), OrderInfoPO::getPayTime, condition.getPayTime());
        wrapper.le(ObjectUtil.isNotNull(condition.getUpdateTime()), OrderInfoPO::getUpdateTime, condition.getUpdateTime());
        wrapper.like(StringUtils.isNotEmpty(condition.getName()), OrderInfoPO::getMassagistName, condition.getName());
        wrapper.like(StringUtils.isNotEmpty(condition.getUserPhone()), OrderInfoPO::getUserPhone, condition.getUserPhone());
        wrapper.inSql(condition4Massagist(condition), OrderInfoPO::getMassagistWithdrawId, getSqlQuery4Massagist(condition));
        wrapper.inSql(condition4Shop(condition), OrderInfoPO::getShopWithdrawId, getSqlQuery4Shop(condition));
        wrapper.and(StringUtils.isNotEmpty(condition.getBeginSettlementTime()), w -> w.ge(OrderInfoPO::getEndTime, condition.getBeginSettlementTime())
                        .le(OrderInfoPO::getEndTime, condition.getEndSettlementTime())
                        .or()
                        .ge(OrderInfoPO::getCancelTime, condition.getBeginSettlementTime()).le(OrderInfoPO::getCancelTime, condition.getEndSettlementTime()));
        wrapper.eq(ObjectUtil.isNotNull(condition.getMessagistWithdrawId()),OrderInfoPO::getMassagistWithdrawId,condition.getMessagistWithdrawId());
        wrapper.eq(ObjectUtil.isNotNull(condition.getShopWithdrawId()),OrderInfoPO::getShopWithdrawId,condition.getShopWithdrawId());
        wrapper.and(StringUtils.isNotNull(condition.getWithdrawMassagistId()),wq ->
                wq.eq(OrderInfoPO::getMassagistId, condition.getWithdrawMassagistId())
                        .eq(OrderInfoPO::getStatus, OrderStatusEnum.$40.getCode())
                        .isNull(OrderInfoPO::getMassagistWithdrawId)
                        .or()
                        .eq(OrderInfoPO::getMassagistId, condition.getWithdrawMassagistId())
                        .eq(OrderInfoPO::getStatus, OrderStatusEnum.$99.getCode())
                        .ge(OrderInfoPO::getOrderOperStatus, OrderOperStatusEnum.$20.getCode())
                        .isNull(OrderInfoPO::getMassagistWithdrawId));
        wrapper.in(CollectionUtil.isNotEmpty(condition.getOrderIds()), OrderInfoPO::getId, condition.getOrderIds());
        wrapper.and(StringUtils.isNotNull(condition.getWithdrawShopId()),wq ->
                wq.eq(OrderInfoPO::getShopId, condition.getWithdrawShopId())
                        .eq(OrderInfoPO::getStatus, OrderStatusEnum.$40.getCode())
                        .isNull(OrderInfoPO::getShopWithdrawId)
        );
        wrapper.orderByDesc(OrderInfoPO::getCreateTime);
        return wrapper;
    }

    private boolean condition4Massagist(OrderInfoCondition condition) {
       return ObjectUtil.isNotNull(condition.getMassagistWithdrawStatus())
               || StringUtils.isNotEmpty(condition.getMassagistWithdrawBeginTime());
    }

    private boolean condition4Shop(OrderInfoCondition condition) {
        return ObjectUtil.isNotNull(condition.getShopWithdrawStatus())
                || StringUtils.isNotEmpty(condition.getShopWithdrawBeginTime());
    }

    private String getSqlQuery4Massagist(OrderInfoCondition condition) {
        String sql = "select id from s_massagist_withdraw_record";
        if (ObjectUtil.isNotNull(condition.getMassagistWithdrawStatus()) && StringUtils.isEmpty(condition.getMassagistWithdrawBeginTime())) {
            sql += " where status = " + condition.getMassagistWithdrawStatus();
        }
        if (ObjectUtil.isNull(condition.getMassagistWithdrawStatus()) && StringUtils.isNotEmpty(condition.getMassagistWithdrawBeginTime())) {
            sql += " where apply_time >= '" + condition.getMassagistWithdrawBeginTime() + "' and apply_time <= '" + condition.getMassagistWithdrawEndTime() + "'";
        }
        if (ObjectUtil.isNotNull(condition.getMassagistWithdrawStatus()) && StringUtils.isNotEmpty(condition.getMassagistWithdrawBeginTime())) {
            sql += " where status = " + condition.getMassagistWithdrawStatus() + " and apply_time >= '" + condition.getMassagistWithdrawBeginTime()
                    + "' and apply_time <= '" + condition.getMassagistWithdrawEndTime() + "'";
        }
        return sql;
    }

    private String getSqlQuery4Shop(OrderInfoCondition condition) {
        String sql = "select id from s_shop_withdraw_record";
        if (ObjectUtil.isNotNull(condition.getShopWithdrawStatus()) && StringUtils.isEmpty(condition.getShopWithdrawBeginTime())) {
            sql += " where status = " + condition.getShopWithdrawStatus();
        }
        if (ObjectUtil.isNull(condition.getShopWithdrawStatus()) && StringUtils.isNotEmpty(condition.getShopWithdrawBeginTime())) {
            sql += " where apply_time >= '" + condition.getShopWithdrawBeginTime() + "' and apply_time <= '" + condition.getShopWithdrawEndTime() + "'";
        }
        if (ObjectUtil.isNotNull(condition.getShopWithdrawStatus()) && StringUtils.isNotEmpty(condition.getShopWithdrawBeginTime())) {
            sql += " where status = " + condition.getShopWithdrawStatus() + " and apply_time >= '" + condition.getShopWithdrawBeginTime()
                    + "' and apply_time <= '" + condition.getShopWithdrawEndTime() + "'";
        }
        return sql;
    }

    private LambdaUpdateWrapper<OrderInfoPO> updateWrapper(OrderInfoPO po) {
        LambdaUpdateWrapper<OrderInfoPO> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ObjectUtil.isNotNull(po.getId()), OrderInfoPO::getId, po.getId());
        updateWrapper.set(ObjectUtil.isNotNull(po.getMassagistId()), OrderInfoPO::getMassagistId, po.getMassagistId());
        updateWrapper.set(ObjectUtil.isNotNull(po.getIsDel()), OrderInfoPO::getIsDel, po.getIsDel());
        updateWrapper.set(ObjectUtil.isNotNull(po.getStatus()), OrderInfoPO::getStatus, po.getStatus());
        updateWrapper.set(ObjectUtil.isNotNull(po.getSubStatus()), OrderInfoPO::getSubStatus, po.getSubStatus());
        updateWrapper.set(ObjectUtil.isNotNull(po.getOrderOperStatus()), OrderInfoPO::getOrderOperStatus, po.getOrderOperStatus());
        updateWrapper.set(ObjectUtil.isNotNull(po.getArriveTime()), OrderInfoPO::getArriveTime, po.getArriveTime());
        updateWrapper.set(ObjectUtil.isNotNull(po.getDepartTime()), OrderInfoPO::getDepartTime, po.getDepartTime());
        updateWrapper.set(ObjectUtil.isNotNull(po.getTakeTime()), OrderInfoPO::getTakeTime, po.getTakeTime());
        updateWrapper.set(ObjectUtil.isNotNull(po.getStartTime()), OrderInfoPO::getStartTime, po.getStartTime());
        updateWrapper.set(ObjectUtil.isNotNull(po.getEndTime()), OrderInfoPO::getEndTime, po.getEndTime());
        updateWrapper.set(ObjectUtil.isNotNull(po.getCancelTime()), OrderInfoPO::getCancelTime, po.getCancelTime());
        updateWrapper.set(ObjectUtil.isNotNull(po.getPayTime()), OrderInfoPO::getPayTime, po.getPayTime());
        updateWrapper.set(StringUtils.isNotEmpty(po.getExactLocation()), OrderInfoPO::getExactLocation, po.getExactLocation());
        updateWrapper.set(StringUtils.isNotEmpty(po.getCancelReason()), OrderInfoPO::getCancelReason, po.getCancelReason());
        updateWrapper.set(ObjectUtil.isNotEmpty(po.getIsTransfer()), OrderInfoPO::getIsTransfer, po.getIsTransfer());
        updateWrapper.set(StringUtils.isNotEmpty(po.getMassagistName()), OrderInfoPO::getMassagistName, po.getMassagistName());
        updateWrapper.set(StringUtils.isNotEmpty(po.getMassagistPhone()), OrderInfoPO::getMassagistPhone, po.getMassagistPhone());
        updateWrapper.set(ObjectUtil.isNotNull(po.getMassagistWithdrawId()), OrderInfoPO::getMassagistWithdrawId, po.getMassagistWithdrawId());
        updateWrapper.set(ObjectUtil.isNotNull(po.getShopWithdrawId()), OrderInfoPO::getShopWithdrawId, po.getShopWithdrawId());
        return updateWrapper;
    }
}
